package org.jeecg.modules.system.filter;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.net.ssl.HostnameVerifier;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.jasig.cas.client.javafilter.authentication.DefaultGatewayResolverImpl;
import org.jasig.cas.client.javafilter.authentication.GatewayResolver;
import org.jasig.cas.client.javafilter.util.AbstractCasFilter;
import org.jasig.cas.client.javafilter.util.CommonUtils;
import org.jasig.cas.client.javafilter.util.ReflectUtils;
import org.jasig.cas.client.javafilter.validation.Assertion;
import org.jasig.cas.client.javafilter.validation.TicketValidationException;
import org.jasig.cas.client.javafilter.validation.TicketValidator;
import org.jeecg.config.IscConfig;
import org.jeecg.config.LogSingleConfig;

import com.gcloud.mesh.framework.core.SpringUtil;
import com.sgcc.isc.ualogin.client.CASClient;
import com.sgcc.isc.ualogin.client.CASTicket;

import cn.hutool.core.codec.Base64;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public abstract class MyAbstractTicketValidationFilter extends AbstractCasFilter {
	private TicketValidator ticketValidator;

	private boolean redirectAfterValidation = false;

	private boolean exceptionOnValidationFailure = true;

	private boolean useSession = true;

	private String casServerUrlPrefix;

	public String getCasServerUrlPrefix() {
		return this.casServerUrlPrefix;
	}

	public void setCasServerUrlPrefix(String casServerUrlPrefix) {
		this.casServerUrlPrefix = casServerUrlPrefix;
	}

	protected TicketValidator getTicketValidator(FilterConfig filterConfig) {
		return this.ticketValidator;
	}

	private GatewayResolver gatewayStorage = (GatewayResolver) new DefaultGatewayResolverImpl();

	protected HostnameVerifier getHostnameVerifier(FilterConfig filterConfig) {
		String className = getPropertyFromInitParams(filterConfig, "hostnameVerifier", null);
		log.trace("Using hostnameVerifier parameter: " + className);
		String config = getPropertyFromInitParams(filterConfig, "hostnameVerifierConfig", null);
		log.trace("Using hostnameVerifierConfig parameter: " + config);
		if (className != null)
			return (HostnameVerifier) ReflectUtils.newInstance(className, new Object[] { config });
		return null;
	}

	protected void initInternal(FilterConfig filterConfig) throws ServletException {
		setExceptionOnValidationFailure(parseBoolean(getPropertyFromInitParams(filterConfig, "exceptionOnValidationFailure", "true")));
		log.trace("Setting exceptionOnValidationFailure parameter: " + this.exceptionOnValidationFailure);
		setRedirectAfterValidation(parseBoolean(getPropertyFromInitParams(filterConfig, "redirectAfterValidation", "true")));
		log.trace("Setting redirectAfterValidation parameter: " + this.redirectAfterValidation);
		setUseSession(parseBoolean(getPropertyFromInitParams(filterConfig, "useSession", "true")));
		log.trace("Setting useSession parameter: " + this.useSession);
		setTicketValidator(getTicketValidator(filterConfig));
		setCasServerUrlPrefix(getPropertyFromInitParams(filterConfig, "serverUrlPrefix", "true"));
		log.trace("Setting casServerUrlPrefix parameter: " + this.casServerUrlPrefix);
		super.initInternal(filterConfig);
	}

	public void init() {
		super.init();
		CommonUtils.assertNotNull(this.ticketValidator, "ticketValidator cannot be null.");
	}

	protected boolean preFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
		return true;
	}

	protected void onSuccessfulValidation(HttpServletRequest request, HttpServletResponse response, Assertion assertion) {
	}

	protected void onFailedValidation(HttpServletRequest request, HttpServletResponse response) {
	}

	public final void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
		if (!preFilter(servletRequest, servletResponse, filterChain))
			return;
		HttpServletRequest request = (HttpServletRequest) servletRequest;
		HttpServletResponse response = (HttpServletResponse) servletResponse;
		String ticket = CommonUtils.safeGetParameter(request, getArtifactParameterName());
		if (CommonUtils.isNotBlank(ticket)) {
			if (log.isDebugEnabled())
				log.debug("Attempting to validate ticket: " + ticket);
			try {
				Assertion assertion = null;

				try {
					assertion = this.ticketValidator.validate(ticket, constructServiceUrl(request, response));
				} catch (TicketValidationException e1) {
					log.error("TicketValidationException error, login other user", e1);
					LogSingleConfig config = SpringUtil.getBean(LogSingleConfig.class);
					if(config == null || !config.getLoginSingle()) {
						// TODO 需要删除代码
						CASTicket client = CASClient.getTicket(IscConfig.getIscSsoPath(), "suwen1", Base64.encode("Isc$2017."), IscConfig.getServerName());
						if (client != null) {
							ticket = client.getServiceTicket();
						}
						assertion = this.ticketValidator.validate(client.getServiceTicket(), constructServiceUrl(request, response));
						// TODO 需要删除代码
					}else {
						log.error("MyAbstractTicketValidationFilter 401 error", e1);
						request.getSession(true).invalidate();
						// response.sendRedirect(urlToRedirectTo);
						response.setStatus(401);
						return;
					}
				}

				if (log.isDebugEnabled())
					log.debug("Successfully authenticated user: " + assertion.getPrincipal().getName());
				request.setAttribute("_const_cas_assertion_", assertion);
				if (this.useSession)
					request.getSession().setAttribute("_const_cas_assertion_", assertion);
				onSuccessfulValidation(request, response, assertion);
				if (this.redirectAfterValidation) {
					log.debug("Redirecting after successful ticket validation.");
					response.sendRedirect(constructServiceUrl(request, response));
					return;
				}
			} catch (Exception e) {
				log.error("MyAbstractTicketValidationFilter 401 error", e);
				String serviceUrl = constructServiceUrl(request, response);
				String casServerloginUrl = this.casServerUrlPrefix;
				if (!casServerloginUrl.endsWith("login"))
					casServerloginUrl = String.valueOf(casServerloginUrl) + "login";
				String urlToRedirectTo = CommonUtils.constructRedirectUrl(casServerloginUrl, getServiceParameterName(), serviceUrl, false, false);
				request.getSession(true).invalidate();
				// response.sendRedirect(urlToRedirectTo);
				response.setStatus(401);
				return;
			}
		}
		filterChain.doFilter((ServletRequest) request, (ServletResponse) response);
	}

	public final void setTicketValidator(TicketValidator ticketValidator) {
		this.ticketValidator = ticketValidator;
	}

	public final void setRedirectAfterValidation(boolean redirectAfterValidation) {
		this.redirectAfterValidation = redirectAfterValidation;
	}

	public final void setExceptionOnValidationFailure(boolean exceptionOnValidationFailure) {
		this.exceptionOnValidationFailure = exceptionOnValidationFailure;
	}

	public final void setUseSession(boolean useSession) {
		this.useSession = useSession;
	}

	private String readResponseBody(InputStream istream) throws IOException {
		ByteArrayOutputStream bout = new ByteArrayOutputStream();
		byte[] buf = new byte[1024];
		int num = 0;
		while ((num = istream.read(buf)) != -1)
			bout.write(buf, 0, num);
		byte[] ret = bout.toByteArray();
		return new String(ret);
	}
}
